package edu.uwlax.cs.oayonlinestore.client.utils;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.user.client.History;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

import edu.uwlax.cs.oayonlinestore.client.gui.shared.Const.CreditCards;
import edu.uwlax.cs.oayonlinestore.client.utils.Table.AddRow;

public abstract class FormTable extends VerticalPanel {

	public final class AddressEdit implements FormTemplate {
		private TextBox city = new TextBox();

		private TextBox state = new TextBox();

		private TextBox street = new TextBox();

		private TextBox zip = new TextBox();

		public AddressEdit() {
			super();
		}

		public void addInputs(FormTable form) {
			addInput("City", city);
			addInput("Street", street);
			addInput("State", state);
			addInput("ZIP", zip);
		}

		public TextBox getCity() {
			return city;
		}

		public TextBox getState() {
			return state;
		}

		public TextBox getStreet() {
			return street;
		}

		public TextBox getZip() {
			return zip;
		}
	}

	private class ButtonClickListener implements ClickListener {
		public void onClick(Widget arg0) {
			error.clear();
			try {
				onButtonClicked();
			} catch (Exception e) {
				error.addError(e);
			}
			if (!hasSaveHandler)
				tryOpenAnchor();
		}
	}

	public final class CreditCardEdit implements FormTemplate {

		private DateEdit expDate = new DateEdit(false, false);

		private SelectionList institute = new SelectionList(CreditCards.ALL);

		private TextBox no = new TextBox();

		public void addInputs(FormTable form) {
			addInput("Credit Card Institute", institute);
			addInput("Credit Card No.", no);
			addInput("Credit Card Expiration Date", expDate);
		}

		public DateEdit getExpDate() {
			return expDate;
		}

		public SelectionList getInstitute() {
			return institute;
		}

		public TextBox getNo() {
			return no;
		}
	}

	public static interface Enablebar {
		public void setEnabled(boolean val);
	}

	public interface FormTemplate {
		public void addInputs(FormTable form);
	}

	public class LoadHandler implements AsyncCallback {
		private LoadHandler() {
			super();
			setEnabled(false);
			status.setText("Loading Data...");
			error.clear();
		}

		public void onFailure(Throwable arg0) {
			setEnabled(true);
			addError(arg0);
			// throw new RuntimeException("Error loading data for a form",
			// arg0);
		}

		public void onSuccess(Object arg0) {
			onDataLoad(arg0);
			setEnabled(true);
			status.setText("");
		}
	}

	private boolean hasSaveHandler = false;

	public class SaveHandler implements AsyncCallback {

		private SaveHandler() {
			super();
			hasSaveHandler = true;
			status.setText("Saving data...");
			setEnabled(false);
		}

		public void onFailure(Throwable arg0) {
			setEnabled(true);
			addError(arg0);
		}

		public void onSuccess(Object arg0) {
			status.setText("Data saved successfully.");
			error.clear();
			setEnabled(true);
			onDataSaved();
			tryOpenAnchor();
		}
	}

	public static final String STYLE_BUTTON = "FormTableButton";

	public static final String STYLE_SECTION_HEADER = "FormTableSection";

	private String anchor = null;

	private Button button = new Button();

	private boolean enabled = true;

	private ErrorText error = new ErrorText();

	private ArrayList inputs = new ArrayList();

	private Text status = new Text();

	private Table table = new Table();

	public FormTable() {
		super();
		add(status);
		add(error);
		add(table);
	}

	public void addButton(String title, String anchor) {
		this.anchor = anchor;
		button.setText(title);
		button.setStyleName(STYLE_BUTTON);
		inputs.add(button);
		button.addClickListener(new ButtonClickListener());
		AddRow r = table.addRow();
		r.add(button).setColspan(2).setHAlign(r.CENTER);
	}

	public void addError(String msg) {
		error.addError(msg);
	}

	public void addError(Throwable t) {
		error.addError(t);
	}

	public void addInput(FormTemplate templ) {
		templ.addInputs(this);
	}

	public void addInput(String title, Widget input) {
		inputs.add(input);
		AddRow r = table.addRow();
		r.add(title + ":").setHAlign(r.RIGHT);
		r.add(input);
	}

	public void addLine(Widget line) {
		AddRow r = table.addRow();
		r.add(line).setColspan(2);
	}

	public void addSection(String title) {
		FlexTable t = new FlexTable();
		t.setStyleName(STYLE_SECTION_HEADER);
		t.setWidth("100%");
		t.setHTML(1, 0, "<hr>");
		t.setHTML(1, 1, "<nobr>" + title + "</nobr>");
		t.setHTML(1, 2, "<hr>");
		t.getCellFormatter().setWidth(0, 0, "50%");
		t.getCellFormatter().setWidth(0, 2, "50%");
		addLine(t);
	}

	public void addText(String title, String text) {
		AddRow r = table.addRow();
		r.add(title + ":").setHAlign(r.RIGHT);
		r.add(text);
	}

	public LoadHandler getLoadHandler() {
		return new LoadHandler();
	}

	public SaveHandler getSaveHandler() {
		return new SaveHandler();
	}

	public boolean hasErrors() {
		return !error.isEmpty();
	}

	public boolean isEnabled() {
		return enabled;
	}

	public abstract void onButtonClicked() throws Exception;

	public abstract void onDataLoad(Object data);

	public void onDataSaved() {
	}

	public void setEnabled(boolean en) {
		enabled = en;
		for (Iterator i = inputs.iterator(); i.hasNext();) {
			Object o = i.next();
			if (o instanceof FocusWidget)
				((FocusWidget) o).setEnabled(en);
			else if (o instanceof Enablebar)
				((Enablebar) o).setEnabled(en);
		}
	}

	private void tryOpenAnchor() {
		if (!hasErrors() && (anchor != null) && (anchor.length() != 0))
			History.newItem(anchor);
	}
}
